

import json
import socket
from threading import Thread
import muliticast_accepter
from util.util_net import NetUtil

class Client:
    def __init__(self, 
                 callback_search : callable = None, 
                 callback_modify  : callable = None,
                 callback_time_set : callable  = None, 
                 callback_time_get : callable  = None, 
                 multicast_group = "239.1.1.100", port = 5555):
        self.multicast_group = multicast_group
        self.port = port
        self.callback_search = callback_search
        self.callback_modify = callback_modify
        self.callback_time_set = callback_time_set
        self.callback_time_get = callback_time_get
        self.accepter = muliticast_accepter.MulticastReceiver(self.multicast_group, self.port, self.__on_data_received)
        self.id = NetUtil.generate_serial()
        self.recv_thread = Thread(target=self.accepter.start, daemon=True)

    def getId(self):
        return self.id

    def start(self):
        self.recv_thread.start()

    def stop(self):
        self.accepter.stop()
        if self.recv_thread.is_alive():
            self.recv_thread.join()

    def __create_socket(self) -> socket.socket:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 64)
        return sock

    def __send_request(self, url: str, data: dict) -> None:
        try:
            sock = self.__create_socket()
            request = {
                "type": "request",
                "url": url,
                "data": data
            }
            request_json = json.dumps(request)
            # print(f"Type of request_json: {type(request_json)}")  # 调试信息
            encoded_data = request_json.encode("utf-8")  # 显式调用 encode
            # print(f"Type of encoded_data: {type(encoded_data)}")  # 调试信息
            sock.sendto(encoded_data, (self.multicast_group, self.port))
        except Exception as e:
            print(f"Failed to send request to {url}: {e}")
        finally:
            if 'sock' in locals():
                sock.close()



    def sendDeviceSearch(self) -> None:
        self.__send_request("/device/search", {"client_id": self.id})

    def sendDeviceModify(self, serial: str, name: str, remark: str, ip: str, submask: str, gateway: str, ntp_url: str) -> None:
        self.__send_request("/device/modify", {
            "client_id": self.id,
            "serial": serial,
            "name": name,
            "remark": remark,
            "ip": ip,
            "submask": submask,
            "gateway": gateway,
            "ntp_url": ntp_url
        })

    def sendDeviceTimeSet(self, serial: str, timestamp_ms: int) -> None:
        self.__send_request("/device/time/set", {
            "client_id": self.id,
            "serial": serial,
            "timestamp_ms": timestamp_ms,
        })

    def sendDeviceTimeGet(self, serial: str) -> None:
        self.__send_request("/device/time/get", {
            "client_id": self.id,
            "serial": serial,
        })

    def __on_data_received(self, data : bytes, addr):
        try:
            res_text = data.decode("utf-8", errors="ignore")
            req_json = json.loads(res_text)

            if (req_json["type"] == "request"):
                # logger.debug(f"Received request packet: {req_json}, ignored")
                return
            if (req_json["type"] == "response") and (req_json["client_id"] == self.id):
                if (req_json["url"] == "/device/search"):
                    self.__on_handle_device_search(req_json["data"])
                elif (req_json["url"] == "/device/modify"):
                    self.__on_handle_device_modify(req_json["data"])
                elif (req_json["url"] == "/device/time/set"):
                    self.__on_handle_device_time_set(req_json["data"])
                elif (req_json["url"] == "/device/time/get"):
                    self.__on_handle_device_time_get(req_json["data"])
                else:
                    self.log(f"Unknown response: {req_json}")
            # else:
                # logger.warning(f"Received unknown packet: {req_json}")
        except Exception as e:
            print(f"Error processing received data: {e}")

    def __on_handle_device_search(self, data):
        if hasattr(self, 'callback_search') and self.callback_search is not None:
            self.callback_search(data)

    def __on_handle_device_modify(self, data):
        if hasattr(self, 'callback_modify') and self.callback_modify is not None:
            self.callback_modify(data)

    def __on_handle_device_time_set(self, data):
        if hasattr(self, 'callback_time_set') and self.callback_time_set is not None:
            self.callback_time_set(data)

    def __on_handle_device_time_get(self, data):
        if hasattr(self, 'callback_time_get') and self.callback_time_get is not None:
            self.callback_time_get(data)


if __name__ == "__main__":
    client = Client()

    input("按任意键退出")



